Documentación del test step CheckHoverContrast
Visión general
El test step CheckHoverContrast verifica que los elementos interactivos cumplan los requisitos de WCAG 2.1 SC 1.4.11 (Contraste no textual) para los cambios de estado en hover. En concreto, comprueba que cuando el cambio de color es la única pista visual que indica el estado hover, la relación de contraste entre el estado por defecto y el estado hover sea de al menos 3:1.
Propósito
Los controles interactivos suelen cambiar de aspecto al pasar el cursor para indicar que son interactivos. Cuando ese cambio depende únicamente del color (sin otras pistas visuales como un cambio de cursor, una sombra o un borde), las personas con baja visión pueden no percibir el cambio de estado. Este test garantiza que haya contraste suficiente entre ambos estados.
Referencia WCAG
- Criterio de conformidad: 1.4.11 Contraste no textual (Nivel AA)
- Requisito: la información visual necesaria para identificar componentes de interfaz y sus estados debe tener una relación de contraste de al menos 3:1 frente a los colores adyacentes.
- Se aplica a: cambios de estado en hover donde el color es el único indicador visual.
Referencias
Cómo funciona
1. Selección de elementos
El test se aplica a los elementos de visually_graphic_elements (iconos, botones con gráficos), ya que son los que con más probabilidad tienen estados hover basados solo en color y sin cambio de cursor.
2. Filtros previos (condiciones de descarte)
Antes de evaluar, se descartan los elementos que:
- No son visibles.
- Están deshabilitados (atributo
disabledoaria-disabled="true").
3. Detección de otras pistas visuales
El test solo se aplica cuando el cambio de color es el único indicador visual. Se descartan los elementos que presentan otras pistas:
| Pista visual | Cómo se detecta |
|---|---|
| Cursor interactivo | pointer, hand, grab, etc. en el estado por defecto o en hover |
| Cambio de cursor | Cualquier cambio de cursor a un valor con significado |
| Aparición de sombra | Se añade box-shadow o text-shadow en hover |
| Cambios de borde | Cambia el grosor, el estilo o la visibilidad del borde |
| Cambios de transform | Cambia la propiedad transform (escala, rotación, etc.) |
| Cambios de tamaño | Las dimensiones del elemento cambian más de 2 px |
| Transforms en SVG | Transform en elementos SVG hijos |
| Cambios de outline | Cambia el grosor o el estilo del outline |
| Decoración de texto | Cambios de subrayado o tachado |
| Cambios de opacidad | Diferencia de opacidad mayor de 0,1 |
| Estilos por defecto del navegador | Estilado de hover propio del navegador (por ejemplo, el subrayado de los enlaces) |
4. Prioridad de extracción de color
Para elementos gráficos (iconos), los colores se extraen en este orden de prioridad:
- Colores de los SVG hijos —
fillystrokede<svg>,<path>,<circle>, etc. - Propiedades gráficas —
fill,stroke,colordel propio elemento. - Propiedades del contenedor —
background-colory colores de borde.
Así se mide el cambio de color del control, no el del fondo.
5. Comparación basada en capturas
El test usa una comparación de capturas de pantalla:
- Desplazar el elemento hasta que sea visible.
- Capturar el estado por defecto.
- Poner el cursor sobre el elemento (espera de 300 ms para que se apliquen los estilos).
- Capturar el estado hover.
- Comparar las capturas para detectar el cambio visual.
- Calcular la relación de contraste mediante análisis de imagen, apoyado en las pistas de color obtenidas del CSS.
6. Cálculo del contraste
La relación de contraste se calcula entre:
- Color del estado por defecto: el color del elemento antes del hover.
- Color del estado hover: el color del elemento después del hover.
La comparación usa get_hover_contrast_ratio(), que:
- Analiza las diferencias de píxeles entre capturas.
- Usa las pistas de color del CSS cuando están disponibles.
- Recurre al análisis del color dominante cuando no las hay.
Formato del reporte
Cuando se encuentra una violación:
| Campo | Valor |
|---|---|
| Title | «Insufficient contrast against the control and background» |
| Description | El <nombre de color> (#hex) del control «<nombre del control>» en hover no alcanza el contraste mínimo frente al fondo <nombre de color> (#hex), con una relación de <relación de contraste>. |
| Failed WCAG CP | 1.4.11 |
Configuración
CheckHoverContrast(
debug=False, # Activa el log de depuración
target_key="visually_graphic_elements" # Clave en context.data con los elementos a revisar
)Constantes
WCAG_NON_TEXT_CONTRAST_THRESHOLD: 3.0 (relación de contraste mínima exigida).INTERACTIVE_CURSOR_VALUES: conjunto de valores de cursor que se consideran pista visual (pointer,hand,grab, etc.).
Limitaciones
1. Imágenes de fondo
- Problema: si un elemento usa
background-imageen lugar debackground-color, la extracción de color vía CSS no puede obtener el color. - Mitigación: la comparación por capturas sigue funcionando analizando los píxeles renderizados, pero la precisión puede bajar.
- Caso límite: si el hover cambia la propia imagen de fondo (y no solo superpone un color), el cálculo de contraste puede no ser exacto.
2. Degradados complejos
- Problema: los elementos con fondo en degradado (
linear-gradient,radial-gradient) no tienen un único valor de color. - Mitigación: el análisis de la captura intenta encontrar los colores dominantes, pero los resultados pueden variar.
3. Pseudoelementos
- Problema: los efectos de hover aplicados mediante
::beforeo::afterpueden no ser detectados porget_all_styles(). - Mitigación: la comparación de capturas sí detecta el cambio visual, aunque falten las pistas de color del CSS.
4. Animaciones y transiciones
- Problema: en elementos con transiciones CSS, la transición puede no haber terminado cuando se toma la captura del hover.
- Mitigación: se espera 300 ms tras el hover, pero las transiciones muy lentas pueden no completarse.
5. Efectos de hover por JavaScript
- Problema: los efectos disparados por eventos
mouseenterde JavaScript pueden tener problemas de sincronización. - Mitigación: la espera de 300 ms ayuda, pero las interacciones complejas pueden no capturarse por completo.
6. Elementos interactivos anidados
- Problema: los botones o enlaces anidados pueden tener efectos de hover que interfieren entre sí.
- Comportamiento actual: cada elemento se evalúa de forma individual.
7. Contenido dinámico
- Problema: los elementos que cargan contenido al hacer hover (tooltips, desplegables) pueden alterar las capturas.
- Mitigación: normalmente presentan otras pistas visuales, por lo que se descartan.
8. Iconos con sprites SVG
- Problema: los iconos que usan sprites SVG (
<use xlink:href>) pueden tener los colores definidos en el sprite y no en el elemento. - Mitigación:
_get_svg_child_colors()intenta extraer los colores de los elementos hijos.
9. Fuentes de iconos
- Problema: las fuentes de iconos (FontAwesome, etc.) usan la propiedad
color, que sí se comprueba, pero puede heredarse de un elemento padre. - Mitigación: la propiedad CSS
colorestá incluida entre las propiedades de color gráficas.
10. Modo de alto contraste
- Problema: quienes usan el modo de alto contraste de Windows pueden ver colores distintos de los evaluados.
- Comportamiento actual: el test se ejecuta con el renderizado estándar del navegador.
Fuera de alcance
Este test step no evalúa:
- Componentes deshabilitados: se descartan automáticamente.
- Elementos con otras pistas visuales: cambios de cursor, sombras, bordes, etc.
- Contraste de texto: corresponde a WCAG 1.4.3, no a 1.4.11.
- Contraste del estado de foco: lo cubre
CheckFocusIndicatorContrast. - Estados active y visited: solo se evalúa el estado hover.
- Colores de hover por defecto del navegador: se descartan por ser responsabilidad del navegador.
Dependencias
Módulos internos
BaseTestStep,TestStepResult— infraestructura base de tests.Element— envoltorio del elemento de página, con capacidades de hover y captura.BaseLogger— infraestructura de logging.
Funciones de utilidad
src.utils.color— utilidades de parseo y conversión de color.src.vision.comparison— comparación de imágenes y cálculo de contraste.compare_byte_images()— detecta diferencias visuales.get_hover_contrast_ratio()— calcula el contraste entre estados.
Ejemplo de uso
from src.a11y_test_definitions.test_step.visually_disabled.CheckHoverContrast import CheckHoverContrast
# Crear el test step
hover_contrast_check = CheckHoverContrast(debug=True)
# Ejecutarlo como parte de un test case
result = hover_contrast_check.run(context, logger)Modo depuración
Con debug=True, el test muestra información detallada paso a paso:
[CheckHoverContrast] Step 1: Checking visibility...
[CheckHoverContrast] Step 2: Checking disabled state...
[CheckHoverContrast] Step 3: Getting default cursor/styles...
[CheckHoverContrast] -> Default cursor: auto
[CheckHoverContrast] -> Default SVG colors: {'fill': 'rgb(102, 102, 102)', 'stroke': ''}
[CheckHoverContrast] Step 4: Scrolling into view and taking default screenshot...
[CheckHoverContrast] Step 5: Hovering element...
[CheckHoverContrast] Step 6: Getting hover cursor...
[CheckHoverContrast] -> Hover cursor: auto
[CheckHoverContrast] Step 7: Getting hover styles...
[CheckHoverContrast] -> Hover SVG colors: {'fill': 'rgb(0, 102, 204)', 'stroke': ''}
[CheckHoverContrast] Step 10: Comparing screenshots...
[CheckHoverContrast] Step 11: Extracting color hints...
[CheckHoverContrast] -> CSS colors: default=(102, 102, 102), hover=(0, 102, 204)
[CheckHoverContrast] Step 12: Calculating contrast ratio...
[CheckHoverContrast] -> Contrast: 2.45:1 (fg=(0, 102, 204), bg=(102, 102, 102))Historial de versiones
| Versión | Cambios |
|---|---|
| 1.0 | Implementación inicial con extracción de color vía CSS |
| 1.1 | Extracción de color de SVG hijos para dar soporte a iconos |
| 1.2 | Comparación basada en capturas para mayor robustez |
| 1.3 | Detección completa de pistas visuales (cursor, sombra, transform, etc.) |
